﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Nexpress
{
    public class Application
    {
        private IList<Func<RequestDelegate, RequestDelegate>> middlewareList = new List<Func<RequestDelegate, RequestDelegate>>();
        private readonly List<RouteData> _routersList = new List<RouteData>();

        private RequestDelegate Last = (req, res) =>
        {
            return Task.CompletedTask;
        };
        
        public void Use(Func<Request, Response, RequestDelegate, Task> func)
        {
            Func<RequestDelegate, RequestDelegate> middleware = (next) =>
            {
                return new RequestDelegate((req, res) =>
                {
                    return func(req, res, next);
                });
            };
            middlewareList.Add(middleware);
        }

        public void Run(Func<Request, Response, Task> last)
        {
            Func<RequestDelegate, RequestDelegate> middleware = (next) =>
            {
                return new RequestDelegate(last);
            };
            middlewareList.Add(middleware);
        }

        public void Get(string route, Func<Request, Response, Task> handlers)
        {
            Bind("Get", route, new RequestDelegate(handlers));
        }

        public void Post(string route, Func<Request, Response, Task> handlers)
        {
            Bind("Post", route, new RequestDelegate(handlers));
        }

        public void Put(string route, Func<Request, Response, Task> handlers)
        {
            Bind("Put", route, new RequestDelegate(handlers));
        }

        public void Delete(string route, Func<Request, Response, Task> handlers)
        {
            Bind("Delete", route, new RequestDelegate(handlers));
        }

        private void Bind(string method, string path, RequestDelegate handler)
        {
            var matchRegular = "^" + Regex.Replace(path, @":\w+", @"([^\/]+?)") + "$";
            var paramsName = new Dictionary<string, int>();
            var pathNodes = path.Split('/');

            for (var i = 0; i < pathNodes.Length; i++)
            {
                var pathNode = pathNodes[i];
                if (pathNode.Length > 0 && pathNode[0] == ':')
                {
                    paramsName.Add(pathNode.Substring(1), i);
                }
            }

            _routersList.Add(new RouteData
            {
                Method = method,
                Path = path,
                Params = paramsName,
                RequestHandler=handler,
                MatchRegular = matchRegular
            });
        }

        private void RunKestrel(string host, int port)
        {
            var server = new WebHostBuilder()
                .UseKestrel()
                .UseUrls($"http://{host}:{port}")
                .UseWebRoot("public")
                .Configure(app => app.Run(async (context) =>
                {
                    var path = context.Request.Path.Value;
                    List<RouteData> matchRoutes = new List<RouteData>();
                    foreach (var router in _routersList)
                    {
                        if (!Regex.Match(path, router.MatchRegular).Success) continue;
                        if (!string.Equals(context.Request.Method, router.Method.ToString(),
                                StringComparison.CurrentCultureIgnoreCase)) continue;
                        matchRoutes.Add(router);
                    }
                    if (matchRoutes.Count > 1)
                    {
                        context.Response.ContentType = "text/plain; charset=utf-8";
                        await context.Response.WriteAsync("找到多个匹配的路由!");
                    }
                    else if (matchRoutes.Count == 1)
                    {
                        var request = new Request();
                        request.RawRequest = context.Request;
                        request.Route = matchRoutes[0];
                        var pathNodes = path.Split('/');
                        foreach (var node in matchRoutes[0].Params)
                        {
                            request.Param.Add(node.Key, pathNodes[node.Value]);
                        }
                        context.Response.ContentType = "text/plain; charset=utf-8";
                        Func<RequestDelegate, RequestDelegate> middleware = (next) =>
                        {
                            return matchRoutes[0].RequestHandler;
                        };
                        await Build(matchRoutes[0].RequestHandler).Invoke(request, new Response() { RawResponse = context.Response });
                    }
                    else
                    {
                        context.Response.ContentType = "text/plain; charset=utf-8";
                        await context.Response.WriteAsync("程序已启动");
                    }
                }))
                .Build();

            server.Run();
        }

        public RequestDelegate Build(RequestDelegate last)
        {
            RequestDelegate requestDelegate = last;
            foreach (var item in middlewareList.Reverse())
            {
                requestDelegate = item(requestDelegate);
            }
            return requestDelegate;
        }

        public void Listen(int port)
        {
            RunKestrel("*", port);
        }
    }
}
